feat: add max-char-limit support to chat widget and web chat template#3196
feat: add max-char-limit support to chat widget and web chat template#3196ezkemboi wants to merge 12 commits into
Conversation
Adds character limit enforcement to the OCS chat widget component: - Live character counter with warning/error states - Disabled send button when message exceeds limit - Updated web_chat.html to pass max-char-limit attribute from backend Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
This was separated from #3180 |
📝 WalkthroughWalkthroughThis pull request adds message character limit validation to the chat widget. The feature introduces a new Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
components/chat_widget/src/components/ocs-chat/ocs-chat.tsx (2)
1765-1769: Consider announcing the counter to assistive tech.The live character counter updates silently. Screen-reader users typing near/over the limit won't be notified, and the disabled send button alone won't explain why submission is blocked. Consider adding
aria-live="polite"(androle="status") to the counter, and/or linking the textarea to the counter viaaria-describedbyso the limit state is announced.♿ Suggested change
- {this.maxCharLimit != null && ( - <div class={`char-counter${this.messageTooLong ? ' char-counter-error' : this.messageNearLimit ? ' char-counter-warning' : ''}`}> - {this.messageInput.length} / {this.maxCharLimit} - </div> - )} + {this.maxCharLimit != null && ( + <div + id="ocs-char-counter" + role="status" + aria-live="polite" + class={`char-counter${this.messageTooLong ? ' char-counter-error' : this.messageNearLimit ? ' char-counter-warning' : ''}`} + > + {this.messageInput.length} / {this.maxCharLimit} + </div> + )}And on the textarea (line 1757), add
aria-describedby={this.maxCharLimit != null ? 'ocs-char-counter' : undefined}andaria-invalid={this.messageTooLong || undefined}.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/chat_widget/src/components/ocs-chat/ocs-chat.tsx` around lines 1765 - 1769, Add accessible live region and link it to the textarea: update the character counter element (give it id "ocs-char-counter") to include aria-live="polite" and role="status" so screen readers announce updates, and on the textarea component (referenced where this.maxCharLimit is used) add aria-describedby={this.maxCharLimit != null ? 'ocs-char-counter' : undefined} and aria-invalid={this.messageTooLong || undefined} so the counter is announced and the over-limit state is exposed to assistive tech.
1798-1807: Minor: send-buttontitleonly reflects too-long state.When the button is disabled for other reasons (uploading, typing, empty input),
titleisundefined. That's fine, but note thetitleattribute is not reliably announced by screen readers — thecomposer.messageTooLongreason should also be exposed viaaria-describedby/aria-live(see counter comment above) so keyboard/AT users know why send is blocked. No change strictly required here if the counter getsaria-live.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/chat_widget/src/components/ocs-chat/ocs-chat.tsx` around lines 1798 - 1807, The send-button currently only sets title for the composer.messageTooLong state so other disabled reasons (uploading, typing, empty input) are not conveyed to assistive tech; update the send button (the component rendering the button where title is set) to also reference an aria-describedby ID that points to a live region or descriptive element that reflects composer.messageTooLong and other disable reasons (e.g., uploading, typing, empty input), or ensure the counter element exposing composer.messageTooLong has aria-live so screen readers announce the reason; specifically, add an aria-describedby attribute on the send button pointing to the existing counter/error element (or create a visually-hidden live region) and ensure that element's content is updated when composer.messageTooLong or other disable conditions change so keyboard/AT users are informed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/chat_widget/src/components.d.ts`:
- Line 108: The declaration of the component property versionNumber conflicts
with its `@internal` documentation in ocs-chat.tsx (around the `@Prop`() on the
versionNumber field) — decide whether it should remain part of the public API or
be private; if it should be internal-only, remove the `@Prop`() decorator from the
versionNumber field in ocs-chat.tsx and convert it to an
internal/service-managed property (or use a private class field/getter) and
update any tests and API request usages accordingly, then regenerate the
component types so components.d.ts no longer exposes versionNumber; if it should
stay public, update the JSDoc/comment to explicitly state "for internal
consumers only" and keep the `@Prop`() but regenerate types to reflect the
clarified documentation.
In `@components/chat_widget/src/components/ocs-chat/ocs-chat.tsx`:
- Around line 504-514: Add unit tests for the new character-limit feature in the
OcsChat component: create tests that render OcsChat (or its exported
component/test wrapper) and exercise the message input and send flow for three
cases — (a) when maxCharLimit is unset: assert no character counter is rendered
and sendMessage (triggered via send button or calling sendMessage) succeeds/does
not block; (b) when message length equals maxCharLimit: assert the counter shows
a warning state (e.g., warning class or text), and send remains enabled; (c)
when message length exceeds maxCharLimit: assert the counter shows an error
state, the send button is disabled, and sendMessage does not proceed. Use the
component name OcsChat, the sendMessage handler (or send button/data-testid),
the input change handler (or message input element), and the character counter
element/data-testid to locate DOM nodes; use Jest + React Testing Library
patterns (render, fireEvent/change, getByTestId/queryByText, expect) to
implement assertions.
In `@templates/chatbots/chat/web_chat.html`:
- Line 38: The template uses max_char_limit but neither chatbot_session_view()
nor _chatbot_chat_ui() includes it in the context, so add max_char_limit into
the version_specific_vars dict returned by both functions (ensure the key name
matches the template attribute: max_char_limit) and propagate any existing
config/default logic used for character limits; then add unit tests that render
web_chat.html via both chatbot_session_view and _chatbot_chat_ui to assert the
attribute is present in the rendered HTML and an integration/widget test that
enforces the char limit when sending messages.
---
Nitpick comments:
In `@components/chat_widget/src/components/ocs-chat/ocs-chat.tsx`:
- Around line 1765-1769: Add accessible live region and link it to the textarea:
update the character counter element (give it id "ocs-char-counter") to include
aria-live="polite" and role="status" so screen readers announce updates, and on
the textarea component (referenced where this.maxCharLimit is used) add
aria-describedby={this.maxCharLimit != null ? 'ocs-char-counter' : undefined}
and aria-invalid={this.messageTooLong || undefined} so the counter is announced
and the over-limit state is exposed to assistive tech.
- Around line 1798-1807: The send-button currently only sets title for the
composer.messageTooLong state so other disabled reasons (uploading, typing,
empty input) are not conveyed to assistive tech; update the send button (the
component rendering the button where title is set) to also reference an
aria-describedby ID that points to a live region or descriptive element that
reflects composer.messageTooLong and other disable reasons (e.g., uploading,
typing, empty input), or ensure the counter element exposing
composer.messageTooLong has aria-live so screen readers announce the reason;
specifically, add an aria-describedby attribute on the send button pointing to
the existing counter/error element (or create a visually-hidden live region) and
ensure that element's content is updated when composer.messageTooLong or other
disable conditions change so keyboard/AT users are informed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 443e84e1-e874-4b8f-93e7-deaac8c360eb
📒 Files selected for processing (7)
components/chat_widget/src/assets/translations/en.jsoncomponents/chat_widget/src/components.d.tscomponents/chat_widget/src/components/ocs-chat/ocs-chat.csscomponents/chat_widget/src/components/ocs-chat/ocs-chat.tsxcomponents/chat_widget/src/components/ocs-chat/readme.mdcomponents/chat_widget/src/services/chat-session-service.tstemplates/chatbots/chat/web_chat.html
There was a problem hiding this comment.
@ezkemboi there are a lot of formatting changes. Are these coming from your IDE? I don't see them when I run npm run lint. I'm not opposed to them but they should be separated from functional changes if possible otherwise it's hard for reviewers to distinguish what is a formatting change and what is a functional change.
I do notice that we have a prettier config file but it isn't hooked up. I'll hook this up and run it to apply the formatting changes in a separate PR which you can then merge into yours. (now merged: #3234)
Adds an explicit prettier dependency, npm scripts (`format`, `format:check`), a `.prettierignore` for Stencil-generated files, and a repo-wide pre-commit hook scoped to the chat widget. Also renames the deprecated `jsxBracketSameLine` key in `.prettierrc.json` to `bracketSameLine`. Without enforcement, contributors' IDEs were silently auto-formatting on save against the existing `.prettierrc.json`, producing large unrelated diffs in otherwise small PRs (e.g. dimagi#3196). This makes formatting deterministic across contributors and CI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d-message-payloads-widget
I am fixing the lint issue on this. |
|
Waiting for #3180 before getting merged or reviewed. |
Removed maxCharLimit property and resolved merge conflicts.
Removed the maxCharLimit property from the widget's component type definition.
|
@snopoke, I will have a look at this in the evening and make necessary updates. |
…d-message-payloads-widget
Product Description
Adds a live character counter and send-button guard to the OCS chat widget. Users see a warning as they approach the character limit and are blocked from sending once they exceed it, giving early feedback instead of a backend error.
Technical Description
Part of #3180 (widget changes separated per changelog/docs automation requirements).
Changes are confined to the StencilJS chat widget component (
components/chat_widget/):ocs-chat.tsx: Reads the newmaxCharLimitprop (passed from the Django host page). Alpine.js-style reactive gettersmessageTooLong/messageNearLimitdrive warning/error CSS classes on the counter and disable the send button when the limit is exceeded.ocs-chat.css: Adds.char-counter,.char-counter--warning, and.char-counter--errorstyles.chat-session-service.ts: Propagates themaxCharLimitvalue through the service layer.components.d.ts: Autogenerated Stencil type — addsmaxCharLimit?: numberprop declaration.templates/chatbots/chat/web_chat.html: Passesmax-char-limit="{{ max_char_limit }}"attribute to<open-chat-studio-widget>when the backend provides a limit.assets/translations/en.json: Adds i18n key for the counter label.The backend that computes
max_char_limitand the HTMX input bar changes live in the companion PR #3180.Demo
See PR #3180 for screenshots of the character counter and error state in the web chat UI.
Docs and Changelog
Widget change exposing a new
maxCharLimitprop on<open-chat-studio-widget>. Changelog entry: the chat widget now enforces a configurable character limit with a live counter.